// NAnt - A .NET build tool
// Copyright (C) 2001-2002 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Tom Jordan (tdjordan@users.sourceforge.net)
// Giuseppe Greco (giuseppe.greco@agamura.com)

using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;

using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Util;

using NAnt.DotNet.Types;

namespace NAnt.DotNet.Tasks {
    /// <summary>
    /// Compiles Visual J# programs using vjc, Microsoft's J# compiler.
    /// </summary>
    /// <remarks>
    /// <note>
    /// In order to have <see cref="VjcTask" /> generate manifest resource names
    /// that match those generated by Microsoft Visual Studio.NET, the value of
    /// the <see cref="ResourceFileSet.Prefix" /> attribute of the &lt;<see cref="CompilerBase.ResourcesList" />&gt;
    /// element should match the "Default Package" of the J#.NET project, and 
    /// the value of the <see cref="ResourceFileSet.DynamicPrefix" /> attribute
    /// should be set to &quot;<see langword="true" />&quot;.
    /// </note>
    /// </remarks>
    /// <example>
    ///   <para>Compile a "HelloWorld" application, including embedded resources.</para>
    ///   <code>
    ///     <![CDATA[
    /// <vjc target="exe" output="helloworld.exe" debug="true">
    ///     <sources>
    ///         <include name="helloworld.jsl" />
    ///     </sources>
    ///     <resources prefix="HelloWorld" dynamicprefix="true">
    ///         <include name="**/*.resx" />
    ///     </resources>
    ///     <references>
    ///         <include name="System.dll" />
    ///         <include name="System.Data.dll" />
    ///         <include name="System.Drawing.dll" />
    ///         <include name="System.Windows.Forms.dll" />
    ///         <include name="System.Xml.dll" />
    ///     </references>
    /// </vjc>
    ///     ]]>
    ///   </code>
    /// </example>
    [TaskName("vjc")]
    [ProgramLocation(LocationType.FrameworkDir)]
    public class VjcTask : CompilerBase {
        #region Private Instance Fields

        private string _baseAddress;
        private DebugOutput _debugOutput = DebugOutput.None;
        private bool _secureScoping;
        private string _x;
        private string _libPath;
        private string _jcpa;
        private string _codepage;
        private string _warningLevel;

        #endregion Private Instance Fields

        #region Private Static Fields

        private static Regex _classNameRegex = new Regex(@"^((?<comment>/\*.*?(\*/|$))|[\s\.\{]+|class\s+(?<class>\w+)|(?<keyword>\w+))*");
        private static Regex _namespaceRegex = new Regex(@"^((?<comment>/\*.*?(\*/|$))|[\s\.\{]+|package\s+(?<namespace>(\w+(\.\w+)*)+)|(?<keyword>\w+))*");
     
        #endregion Private Static Fields

        #region Public Instance Properties

        /// <summary>
        /// The preferred base address at which to load a DLL. The default base 
        /// address for a DLL is set by the .NET Framework common language 
        /// runtime.
        /// </summary>
        /// <value>
        /// The preferred base address at which to load a DLL.
        /// </value>
        /// <remarks>
        /// This address can be specified as a decimal, hexadecimal, or octal 
        /// number. 
        /// </remarks>
        public string BaseAddress {
            get { return _baseAddress; }
            set { _baseAddress = StringUtils.ConvertEmptyToNull(value); }
        }

        /// <summary>
        /// Specifies the type of debugging information generated by the 
        /// compiler. The default is <see cref="NAnt.DotNet.Types.DebugOutput.None" />.
        /// </summary>
        [TaskAttribute("debug")]
        public DebugOutput DebugOutput {
            get { return _debugOutput; }
            set { _debugOutput = value; }
        }

        /// <summary>
        /// No longer expose this to build authors. Use <see cref="DebugOutput" />
        /// instead.
        /// </summary>
        public override bool Debug {
            get { return DebugOutput != DebugOutput.None; }
            set { DebugOutput = DebugOutput.Enable; }
        }

        /// <summary>
        /// Specifies whether package-scoped members are accessible outside of 
        /// the assembly. In other words, package scope is treated as assembly 
        /// scope when emitting metadata. The default is <see langword="false" />.
        /// </summary>
        /// <value>
        /// <see langword="true" /> if the option should be passed to the compiler; 
        /// otherwise, <see langword="false" />.
        /// </value>
        /// <remarks>
        /// <para>
        /// Corresponds to the <c>/securescoping</c> flag.
        /// </para>
        /// <para>
        /// <a href="ms-help://MS.VSCC/MS.VJSharp/dv_vjsharp/html/vjgrfsecurescopingmakepackage-scopedmembersinaccessibleoutsideassembly.htm">See the Visual J# Reference for details.</a>
        /// </para>
        /// </remarks>
        /// <example>
        /// <code><![CDATA[<vjc securescoping='true'/>]]></code>
        /// </example>
        [TaskAttribute("securescoping")]
        [BooleanValidator()]
        public bool SecureScoping {
            get { return _secureScoping; }
            set { _secureScoping = value; }
        }
       
        /// <summary>
        /// Specifies whether to disable language extensions.
        /// </summary>
        /// <value>
        /// The value of this property must be either <c>all</c>, <c>net</c>, 
        /// or an empty string.
        /// </value>
        /// <remarks>
        /// <para>
        /// Corresponds to the <c>/x</c> flag.
        /// </para>
        /// <para>
        /// <a href="ms-help://MS.VSCC/MS.VJSharp/dv_vjsharp/html/vjgrfxdisablelanguageextensions.htm">See the Visual J# Reference for details.</a>
        /// </para>
        /// </remarks>
        /// <example>
        /// <para>To disable only the .NET Framework extensions:<c><![CDATA[
        /// <vjc x='net'/>
        /// ]]></c></para>
        /// <para>To disable the .NET Framework extensions and the VJ++ 6.0 extensions:<c><![CDATA[
        /// <vjc x='all'/>
        /// ]]></c></para>
        /// </example>
        [TaskAttribute("x")]
        public string X {
            get { return _x; }
            set { _x = StringUtils.ConvertEmptyToNull(value); }
        }
       
        /// <summary>
        /// Specifies the location of assemblies referenced by way of the <c>/reference</c> flag.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Corresponds to the <c>/libpath:dir[;dir2]</c> flag.
        /// </para>
        /// <para>
        /// <a href="ms-help://MS.VSCC/MS.VJSharp/dv_vjsharp/html/vjgrflibpathspecifyassemblyreferencelocations.htm">See the Visual J# Reference for details.</a>
        /// </para>
        /// </remarks>
        [TaskAttribute("libpath")]
        public string LibPath {
            get { return _libPath; }
            set { _libPath = StringUtils.ConvertEmptyToNull(value); }
        }
       
        /// <summary>
        /// Associate Java-language/COM package names.
        /// </summary>
        /// <value>
        /// The value of this propery. must be <c>package=namespace</c>, <c>@filename</c>, 
        /// or an empty string.
        /// </value>
        /// <remarks>
        /// <para>
        /// Corresponds to the <c>/jcpa:package=namespace</c> and <c>/jcpa:@filename</c> flags.
        /// </para>
        /// <para>
        /// <a href="ms-help://MS.VSCC/MS.VJSharp/dv_vjsharp/html/vjgrfjcpaassociatejava-compackages.htm">See the Visual J# Reference for details.</a>
        /// </para>
        /// </remarks>
        /// <example>
        /// <para>Map package 'x' to namespace 'y':<c><![CDATA[
        /// <vjc jcpa='x=y'/>
        /// ]]></c></para>
        /// </example>
        [TaskAttribute("jcpa")]
        public string Jcpa {
            get { return _jcpa; }
            set { _jcpa = StringUtils.ConvertEmptyToNull(value); }
        }
       
        /// <summary>
        /// Specifies the code page to use for all source code files in the 
        /// compilation.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Corresponds with the <c>/codepage</c> flag.
        /// </para>
        /// <para>
        /// <a href="ms-help://MS.VSCC/MS.VJSharp/dv_vjsharp/html/vjlrfcodepagespecifycodepageforsourcecodefiles.htm">See the Visual J# Reference for details.</a>
        /// </para>
        /// </remarks>
        [TaskAttribute("codepage")]
        public string Codepage {
            get { return _codepage; }
            set { _codepage = StringUtils.ConvertEmptyToNull(value); }
        }

        /// <summary>
        /// Specifies the warning level for the compiler to display. Valid values 
        /// are <c>0</c>-<c>4</c>. The default is <c>4</c>.
        /// </summary>
        /// <value>
        /// The warning level for the compiler to display.
        /// </value>
        /// <remarks>
        /// <para>
        /// Corresponds with the <c>/warn</c> option.
        /// </para>
        /// </remarks>
        [TaskAttribute("warninglevel")]
        [Int32Validator(0, 4)]
        public string WarningLevel {
            get { return _warningLevel; }
            set { _warningLevel = StringUtils.ConvertEmptyToNull(value); }
        }

        /// <summary>
        /// Controls which warnings should be reported as errors.
        /// </summary>
        /// <remarks>
        /// Override to avoid exposing this to build authors, as the Visual J#
        /// compiler does not allow control over which warnings should be
        /// reported as errors.
        /// </remarks>
        public override WarningAsError WarningAsError {
            get { return base.WarningAsError; }
        }

        #endregion Public Instance Properties

        #region Override implementation of CompilerBase

        /// <summary>
        /// Reference packages 
        /// </summary>
        /// <remarks>
        /// Override to avoid exposing this to build authors, as the Visual J#
        /// compiler does not support package references.
        /// </remarks>
        public override PackageCollection Packages {
            get { return base.Packages; }
            set { base.Packages = value; }
        }

        /// <summary>
        /// Link the specified modules into this assembly.
        /// </summary>
        /// <remarks>
        /// Override to avoid exposing this to build authors, as the Visual J#
        /// compiler does not support linking modules.
        /// </remarks>
        public override AssemblyFileSet Modules {
            get { return base.Modules; }
            set { base.Modules = value; }
        }

        /// <summary>
        /// Writes module references to the specified <see cref="TextWriter" />.
        /// </summary>
        /// <param name="writer">The <see cref="TextWriter" /> to which the module references should be written.</param>
        protected override void WriteModuleReferences(TextWriter writer) {
            if (Modules.FileNames.Count > 0) {
                Log(Level.Warning, ResourceUtils.GetString("String_JscDoesNotSupportLinkingModules"));
            }
        }

        /// <summary>
        /// Writes the compiler options to the specified <see cref="TextWriter" />.
        /// </summary>
        /// <param name="writer"><see cref="TextWriter" /> to which the compiler options should be written.</param>
        protected override void WriteOptions(TextWriter writer) {
            // the base address for the DLL
            if (BaseAddress != null) {
                WriteOption(writer, "baseaddress", BaseAddress);
            }

            // handle secure scoping.
            if (SecureScoping) {
                WriteOption(writer, "securescoping"); 
            }

            // handle the disable framework extensions option.
            if (X != null) {
                WriteOption(writer, "x", X);
            }

            // handle the libpath option.
            if (LibPath != null ) {
                WriteOption(writer, "libpath", LibPath);
            }

            // handle the jcpa option.
            if (Jcpa != null) {
                WriteOption(writer, "jcpa", Jcpa);
            }

            // handle the codepage option.
            if (Codepage != null) {
                WriteOption(writer, "codepage", Codepage);
            }

            // handle debug builds.
            switch (DebugOutput) {
                case DebugOutput.None:
                    break;
                case DebugOutput.Enable:
                    WriteOption(writer, "debug");
                    WriteOption(writer, "define", "DEBUG");
                    WriteOption(writer, "define", "TRACE");
                    break;
                case DebugOutput.Full:
                    WriteOption(writer, "debug");
                    break;
                case DebugOutput.PdbOnly:
                    WriteOption(writer, "debug", "pdbonly");
                    break;
                default:
                    throw new BuildException(string.Format(CultureInfo.InvariantCulture,
                        ResourceUtils.GetString("NA2011"), DebugOutput), Location);
            }

            if (WarningLevel != null) {
                WriteOption(writer, "warn", WarningLevel);
            }

            // win32res
            if (Win32Res != null) {
                WriteOption (writer, "win32res", Win32Res.FullName);
            }
        }

        /// <summary>
        /// Gets the file extension required by the current compiler.
        /// </summary>
        /// <value>
        /// For the J# compiler, the file extension is always <c>jsl</c>.
        /// </value>
        public override string Extension {
            get { return "jsl"; }
        }

        /// <summary>
        /// Gets the class name regular expression for the language of the 
        /// current compiler.
        /// </summary>
        /// <value>
        /// Class name regular expression for the language of the current 
        /// compiler.
        /// </value>
        protected override Regex ClassNameRegex {
            get { return _classNameRegex; }
        }

        /// <summary>
        /// Gets the namespace regular expression for the language of the 
        /// current compiler.
        ///</summary>
        /// <value>
        /// Namespace regular expression for the language of the current 
        /// compiler.
        /// </value>
        protected override Regex NamespaceRegex {
            get { return _namespaceRegex; }
        }

        /// <summary>
        /// Override to avoid exposing the configuration setting for this
        /// task as Visual J# will never support package references.
        /// </summary>
        /// <value>
        /// <see langword="false" />, as the Visual J# compiler will never
        /// support package references.
        /// </value>
        public override bool SupportsPackageReferences {
            get { return false; }
            set { }
        }

        #endregion Override implementation of CompilerBase
    }
}
